package bueu.bexl.utils;

import java.util.HashMap;

public class ConverterRepository {

	public static final ConverterRepository INSTANCE = new ConverterRepository();
	
	private final HashMap<Class<?>, Converter> map;
	private final Converter enumConverter = new Converter() {
		@SuppressWarnings("unchecked")
		@Override
		public Object convert(final Class<?> toType, final Object value) {
			try {
				if (value instanceof String) {
					return Enum.valueOf((Class<? extends Enum>) toType, (String) value);
				}
	
				return Enum.valueOf((Class<? extends Enum>) toType, value.toString());
			} catch (IllegalArgumentException e) {
				return null;
			}
		}
	};

	private ConverterRepository() {
		this.map = new HashMap<Class<?>, Converter>();
		
		for (PrimitiveConverter converter : PrimitiveConverter.values()) {
			this.map.put(converter.simpleType, converter);
			this.map.put(converter.type, converter);
		}
	}
	
	public final void register(final Class<?> type, final Converter converter) {
		this.map.put(type, converter);
	}
	
	public final Converter find(final Class<?> type) {
		if (type.isEnum()) {
			return this.enumConverter;
		}

		return this.map.get(type);
	}
	
	public enum PrimitiveConverter implements Converter {
		boolean_(boolean.class, Boolean.FALSE) {
			@Override
			public Object convert(final Class<?> toType, final Object value) {
				if (value instanceof Number) {
					return ((Number) value).doubleValue() == 0? Boolean.FALSE : Boolean.TRUE;
				}

				if (value instanceof String) {
					return Boolean.valueOf((String) value);
				}

				return Boolean.valueOf(value.toString());
			}
		},
		char_(char.class, new Character('\0')) {
			@Override
			public Object convert(final Class<?> toType, final Object value) {

				if (value instanceof String) {
					return new Character(((String) value).charAt(0));
				}

				return new Character(value.toString().charAt(0));
			}
		},
		byte_(byte.class, new Byte((byte) 0)) {
			@Override
			public Object convert(final Class<?> toType, final Object value) {
				if (value instanceof Number) {
					return new Byte(((Number) value).byteValue());
				}

				if (value instanceof String) {
					return new Byte((String) value);
				}

				return new Byte(value.toString());
			}
		},
		short_(short.class, new Short((short) 0)) {
			@Override
			public Object convert(final Class<?> toType, final Object value) {
				if (value instanceof Number) {
					return new Short(((Number) value).shortValue());
				}

				if (value instanceof String) {
					return new Short((String) value);
				}

				return new Short(value.toString());
			}
		},
		int_(int.class, new Integer(0)) {
			@Override
			public Object convert(final Class<?> toType, final Object value) {
				if (value instanceof Number) {
					return new Integer(((Number) value).intValue());
				}

				if (value instanceof String) {
					return new Integer((String) value);
				}

				return new Integer(value.toString());
			}
		},
		long_(long.class, new Long(0)) {
			@Override
			public Object convert(final Class<?> toType, final Object value) {
				if (value instanceof Number) {
					return new Long(((Number) value).longValue());
				}

				if (value instanceof String) {
					return new Long((String) value);
				}

				return new Long(value.toString());
			}
		},
		float_(float.class, new Float(0.0f)) {
			@Override
			public Object convert(final Class<?> toType, final Object value) {
				if (value instanceof Number) {
					return new Float(((Number) value).floatValue());
				}

				if (value instanceof String) {
					return new Float((String) value);
				}

				return new Float(value.toString());
			}
		},	
		double_(double.class, new Double(0)) {
			@Override
			public Object convert(final Class<?> toType, final Object value) {
				if (value instanceof Number) {
					return new Double(((Number) value).doubleValue());
				}

				if (value instanceof String) {
					return new Double((String) value);
				}

				return new Double(value.toString());
			}
		};
		
		public final Object zero;
		protected final Class<?> simpleType;
		protected final Class<?> type;
		
		PrimitiveConverter(Class<?> type, Object zero) {
			this.simpleType = type;
			this.type = zero.getClass();
			this.zero = zero;
		}
	}
}
